/* **********************************
 * Heatmap
 * heatmap.pde
 * **********************************
 */
 
// fixation radius in pixels
static final int		FIXATION_RADIUS		= 30;
static final int 	SQ_FIXATION_RADIUS	= 30*30;
static final float	SQ_FIXATION_RADIUS_FLOAT	= (float) SQ_FIXATION_RADIUS;
static final float	GAUSSIAN_FIXATION_C	= 1.0 / 0.15;

class Heatmap
{
	int stimulusNumber;
	int[][] hits;
	int maxValue;
	PImage theMap;
	
	
	Heatmap(int number)
	{
		this.stimulusNumber = number;
		hits = new int[VIS_WIDTH][VIS_HEIGHT];
		
		// zero out the hits
		for (int i = 0; i < VIS_WIDTH; i++)
		{
			for (int j = 0; j < VIS_HEIGHT; j++)
			{
				hits[i][j] = 0;
			}
		}
		
		maxValue = 0;
	}
	
	void addHeatmap(Heatmap another)
	{
		for (int i = 0; i < VIS_WIDTH; i++)
		{
			for (int j = 0; j < VIS_HEIGHT; j++)
			{
				this.hits[i][j] += another.hits[i][j];
				maxValue = max(maxValue, this.hits[i][j]);
			}
		}
	}
	
	boolean read( String filename )
	{
		CachedBinaryReader input = new CachedBinaryReader( filename );
		if (!input.isReady())
		{
			println("Could not open: " + filename);
			return false;
		}
		
		for (int i = 0; i < VIS_WIDTH; i++)
		{
			for (int j = 0; j < VIS_HEIGHT; j++)
			{
				try
				{
					hits[i][j] = input.readInt();
					maxValue = max(maxValue, hits[i][j]);
				}
				catch (EOFException e)
				{
					println("Unexpected EOF in file " + filename);
					return false;
				}
			}
		}
		
		makePicture();
		
		return true;
	}
	
	boolean write( String filename )
	{
		CachedBinaryWriter output = new CachedBinaryWriter( filename );
		if (! output.isReady() )
		{
			println("Could not open file " + filename + " for writing...");
			return false;
		}
		
		for (int i = 0; i < VIS_WIDTH; i++)
		{
			for (int j = 0; j < VIS_HEIGHT; j++)
			{
				output.writeInt( hits[i][j] );
			}
		}
		
		output.flushFile();
		output.closeFile();
		
		return true;
	}
	
	
	void makePicture()
	{
		theMap = createImage(VIS_WIDTH, VIS_HEIGHT, ARGB);
		theMap.loadPixels();
		
		float logMax = log( (float) maxValue);
		float theMax = (float) maxValue;
		
		
		// translate the raster to the picture
		for (int i = 0; i < VIS_WIDTH; i++)
		{
			for (int j = 0; j < VIS_HEIGHT; j++)
			{
				int v = hits[i][j];
				int pIndex = j*VIS_WIDTH + i;
				
				if (v == 0)
				{
					theMap.pixels[ pIndex ] = color(0, 0, 0, 0);
				}
				else
				{
					//float x = log( (float) v );
					//float c = (x / logMax) * 255.0;
					float c = 255.0 * (float) v / theMax;
					theMap.pixels[ pIndex ] = color( round(c), 0, 0, round(map(c, 0.0, 255.0, 50.0, 160.0)) ); 
				}
			}
		}
		
		theMap.updatePixels();
	}
	
	void rasterize( int x, int y, int value )
	{
		int minX = max(0, x - FIXATION_RADIUS);
		int maxX = min(VIS_WIDTH-1, x + FIXATION_RADIUS);
		
		int minY = max(0, y - FIXATION_RADIUS);
		int maxY = min(VIS_HEIGHT-1, y + FIXATION_RADIUS);
		
		for (int i = minX; i <= maxX; i++)
		{
			for (int j = minY; j <= maxY; j++)
			{
				// test circle
				int d = (x-i)*(x-i)+(y-j)*(y-j);
				if ( d <= SQ_FIXATION_RADIUS)
				{
					// calculate a guassian value as a hit
					float g = 1.0 - exp( -sq( (float) d / SQ_FIXATION_RADIUS_FLOAT - 1.0 ) * GAUSSIAN_FIXATION_C );
										
					hits[i][j] += round( (float) value * g );
					maxValue = max(maxValue, hits[i][j]);
				}
			}
		}
	}
	
	PImage getMap()
	{
		return theMap;
	}
}
